package veterinario;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.GregorianCalendar;

/** Classe que define uma clínica com os preços, listas de clientes, médicos e eventos.
 *
 * @author 2011101993 - Hugo Amaro
 * @author 2006130415 - Jorge Martins Alves
 */
public class Clinica {

        private Logger log = new Logger(true);
	private float _precoConsultaEstimacao = 35.00f;
	private float _precoConsultaCriacao = 20.0f;
	private float _precoInternamentoEstimacao = 50.0f;
	private float _precoInternamentoCriacao = 80.0f;
	private float _precoDeslocacao = 6.0f;
        private int idCli = 0;
        private int idMed = 0;
        private int idAni = 0;
        /**
         * ArrayList que contém todos os clientes da clínica, que por sua vez contêm os seus animais
         */
        public ArrayList<Cliente> _clientes = new ArrayList<Cliente>();
        //public ArrayList<Animal> _animais = new ArrayList<Animal>();
        /**
         * ArrayList que contém todos os médicos da clínica
         */
	public ArrayList<Medico> _medicos = new ArrayList<Medico>();
        /**
         * ArrayList que contém todos os Internamentos e todas as Consultas
         */
	public ArrayList<Cobravel> _eventos = new ArrayList<Cobravel>();

        /**
         * Construtor que vai iniciar a clínica chamando o método load() com os dados já guardados, caso contrário, começa tudo a 'zero'.
         */
        public Clinica(){
            if(load()<0)
                log.erro("load dos objectos - valores default e lists vazias em uso");
                
        }

        /** Método que lê dos ficheiros todos os dados guardados da clínica.
         * 
         * @return valor 0 se clínica for inicializada com sucesso, -1 caso contrário.
         */
        final public int load(){
            FicheiroDeObjectos fman = new FicheiroDeObjectos();
            try {
                fman.abreLeitura("clinicaDB");
            } catch (IOException e){
                log.erro("abertura do ficheiro para load inicial");
                return -1;
            }

            try {
                this.idAni = (int)fman.leInt();
                this.idMed = (int)fman.leInt();
                this.idCli = (int)fman.leInt();
                this._precoConsultaEstimacao = (float)fman.leFloat();
                this._precoConsultaCriacao = (float)fman.leFloat();
                this._precoInternamentoEstimacao = (float)fman.leFloat();
                this._precoInternamentoCriacao = (float)fman.leFloat();
                this._precoDeslocacao = (float)fman.leFloat();
            } catch (IOException e) {
                log.erro("leitura dos preços");
                return -1;
            }


            try {
                this._clientes = (ArrayList<Cliente>)fman.leObjecto();
                this._medicos = (ArrayList<Medico>)fman.leObjecto();
                this._eventos = (ArrayList<Cobravel>)fman.leObjecto();
            } catch (ClassNotFoundException e) {
                log.erro("leitura dos arraylists - ClassNotFoundException");
                return -1;
            } catch (IOException e) {
                log.erro("leitura dos arraylists - IOException");
                return -1;
            }

            return 0;
        }

        /** Método que grava nos ficheiros todos os dados da clínica.
         * 
         * @return valor 0 se dados da clínica forem guardados com sucesso, -1 caso contrário.
         */
        final public int save(){
            FicheiroDeObjectos fman = new FicheiroDeObjectos();
            try {
                fman.abreEscrita("clinicaDB");
            } catch (IOException e){
                log.erro("abertura do ficheiro para load inicial");
                return -1;
            }

            try {
                fman.escreveInt(idAni);
                fman.escreveInt(idMed);
                fman.escreveInt(idCli);
                fman.escreveFloat(this._precoConsultaEstimacao);
                fman.escreveFloat(this._precoConsultaCriacao);
                fman.escreveFloat(this._precoInternamentoEstimacao);
                fman.escreveFloat(this._precoInternamentoCriacao);
                fman.escreveFloat(this._precoDeslocacao);
            } catch (IOException e) {
                log.erro("escrita dos preços");
                return -1;
            }


            try {
                fman.escreveObjecto(this._clientes);
                fman.escreveObjecto(this._medicos);
                fman.escreveObjecto(this._eventos);
            } catch (IOException e) {
                log.erro("escrita dos arraylists - IOException");
                return -1;
            }

            return 0;
        }


        /** Método que calcula e devolve o total das receitas da clínica num determinado período de tempo.
         * 
         * @param aDatai    data de início do período de consulta.
         * @param aDataf    data de fim do período de consulta.
         * @return  float com valor da receita total de todos os tipos de consultas.
         */
        public float getTotal(GregorianCalendar aDatai, GregorianCalendar aDataf) {
            float total = 0;
            Cobravel[] asConsultas = getEntreDatas(aDatai, aDataf);
            for(int i=0; i<asConsultas.length; i++){
                total += asConsultas[i].custo();
            }
            return total;
	}

        /** Método que calcula e devolve o total das receitas das consultas de um determinado médico.
         * 
         * @param umMedico  objecto Medico com o médico cujas receitas se quer consultar.
         * @return  float com valor da receita total das consultas do médico.
         */
        public float getTotalMedico(Medico umMedico) {
            float total = 0;
            Consulta[] asConsultas = getConsultasMedico(null, null, umMedico);
            for(int i=0; i<asConsultas.length; i++){
                total += asConsultas[i].custo();
            }
            Internamento[] osInternamentos = getInternamentosMedico(null, null, umMedico);
            for(int i=0; i<osInternamentos.length; i++){
                total += osInternamentos[i].custo();
            }
            return total;
	}

        /** Método que calcula e devolve o total das receitas geradas só pelas consultas (quer na clínica quer no domicílio) num determinado período.
         * 
         * @param aDatai    data de início do período de consulta.
         * @param aDataf    data de fim do período de consulta.
         * @return  float com o valor da receita total das consultas (clínica e domicílio) num determinado período.
         */
        public float getConsultasTotal(GregorianCalendar aDatai, GregorianCalendar aDataf) {
            float total = 0;
            Consulta[] asConsultas = getConsultas(aDatai, aDataf);
            for(int i=0; i<asConsultas.length; i++){
                total += asConsultas[i].custo();
            }
            return total;
	}

        /** Método que calcula e devolve o total das receitas geradas só pelos internamentos num determinado período.
         * 
         * @param aDatai    data de início do período de consulta.
         * @param aDataf    data de fim do período de consulta.
         * @return  float com o valor da receita total dos internamentos na clínica num determinado período.
         */
        public float getInternamentosTotal(GregorianCalendar aDatai, GregorianCalendar aDataf) {
            float total = 0;
            Consulta[] asInternamentos = getInternamentos(aDatai, aDataf);
            for(int i=0; i<asInternamentos.length; i++){
                total += asInternamentos[i].custo();
            }
            return total;
	}

        /** Método get que devolve o próximo id a ser atribuido a clientes da clínica.
         * 
         * @return  inteiro com o número de clientes.
         */
        public int getIdCli(){
            return this.idCli;
        }

        /** Método get que devolve o próximo id a ser atribuido a médicos da clínica.
         * 
         * @return  inteiro com o número de médicos.
         */
        public int getIdMed(){
            return this.idMed;
        }

        /** Método get que devolve o próximo id a ser atribuido a animais da clínica.
         * 
         * @return  inteiro com o próximo id a ser atribuido a animais da clínica.
         */
        public int getIdAni(){
            return this.idAni;
        }

        /** Método para incrementar o id a ser atribuido a animais da clínica.
         * 
         * @return  inteiro com o id a ser atribuido a um novo animal da clínica.
         */
        public int newAnimal(){
            return this.idAni++;
        }

        /** Método para incrementar o id a ser atribuido a clientes da clínica.
         * 
         * @return  inteiro com o id a ser atribuido a um novo cliente da clínica.
         */
        public int newCliente(){
            return this.idCli++;
        }

        /** Método para incrementar o id a ser atribuido a médicos da clínica.
         * 
         * @return  inteiro com o id a ser atribuido a um novo médico da clínica.
         */
        public int newMedico(){
            return this.idMed++;
        }

        /** Método que determina e devolve uma lista com todos os eventos (consultas na clínica ou domicílio e internamentos) num determinado período.
         * 
         * @param aDatai    data de início do período pretendido.
         * @param aDataf    data de fim do período pretendido.
         * @return  array com todos os eventos encontrados.
         */
        public Cobravel[] getEntreDatas(GregorianCalendar aDatai, GregorianCalendar aDataf) {

                if(aDataf==null)
                    aDataf = new GregorianCalendar();
                if(aDatai==null)
                    aDatai = new GregorianCalendar(1,0,1);
                //array temporario para guardar os eventos todos como array
		Cobravel[] entreDatasTemp = new Cobravel[this._eventos.size()];
                this._eventos.toArray(entreDatasTemp);

                //ordena por data de fim do evento
                Arrays.sort(entreDatasTemp);

                //determinamos os ids de fim e inicio dos eventos que se encontram entre as datas passadas
                int idS=0, idF=0;
                for(idS=0; idS<entreDatasTemp.length; idS++){
                    if(entreDatasTemp[idS].getClass()==Internamento.class)
                        if(((Internamento)entreDatasTemp[idS]).isFinalized()){
                            if(((Internamento)entreDatasTemp[idS]).getDataf().compareTo(aDatai)>=0){
                                break;
                            }
                        } else {
                            if(((Internamento)entreDatasTemp[idS]).getDatai().compareTo(aDatai)>=0){
                                break;
                            }
                        }
                    else
                        if(((Consulta)entreDatasTemp[idS]).getDataf().compareTo(aDatai)>=0)
                            break;
                }

                for(idF=idS; idF<entreDatasTemp.length; idF++){
                    if(entreDatasTemp[idS].getClass()==Internamento.class)
                        if(((Internamento)entreDatasTemp[idS]).isFinalized()){
                            if(((Internamento)entreDatasTemp[idS]).getDataf().compareTo(aDataf)>=0){
                                break;
                            }
                        } else {
                            if(((Internamento)entreDatasTemp[idS]).getDatai().compareTo(aDataf)>=0){
                                break;
                            }
                        }
                    else
                        if(((Consulta)entreDatasTemp[idF]).getDataf().compareTo(aDataf)>0)
                            break;
                }

                //log.outln("ids: "+idS+" idf: "+idF);

                Cobravel[] entreDatas = new Cobravel[idF-idS];
                if(idF-idS>0){
                    entreDatas = Arrays.copyOfRange(entreDatasTemp, idS, idF);
                }
                    //this._eventos.subList(idS, idF).toArray(entreDatas);

                return entreDatas;
	}

        /** Método que determina e devolve uma lista só com as consultas (quer na clínica quer no domicílio) num determinado período.
         * 
         * @param aDatai    data de início do período pretendido.
         * @param aDataf    data de fim do período pretendido.
         * @return  array com todas as consultas encontrados.
         */
        public Consulta[] getConsultas(GregorianCalendar aDatai, GregorianCalendar aDataf) {
		Cobravel[] entreDatas = getEntreDatas(aDatai,aDataf);
                ArrayList<Consulta> tempConsultas = new ArrayList<Consulta>();
                for(int i=0; i<entreDatas.length; i++){
                    if(entreDatas[i].getClass()==Consulta.class)
                        tempConsultas.add((Consulta)entreDatas[i]);
                }
                Consulta[] consultasEntreDatas = new Consulta[tempConsultas.size()];

                return tempConsultas.toArray(consultasEntreDatas);
	}

        /** Método que determina e devolve uma lista só com os internamentos num determinado período.
         * 
         * @param aDatai    data de início do período pretendido.
         * @param aDataf    data de fim do período pretendido.
         * @return  array com todos os internamentos encontrados.
         */
        public Internamento[] getInternamentos(GregorianCalendar aDatai, GregorianCalendar aDataf) {
		Cobravel[] entreDatas = getEntreDatas(aDatai,aDataf);
                ArrayList<Consulta> tempInternamento = new ArrayList<Consulta>();
                for(int i=0; i<entreDatas.length; i++){
                    if(entreDatas[i].getClass()==Internamento.class)
                        tempInternamento.add((Consulta)entreDatas[i]);
                }
                Internamento[] InternamentoEntreDatas = new Internamento[tempInternamento.size()];

                return tempInternamento.toArray(InternamentoEntreDatas);
	}

        /** Método que determina e devolve uma lista só com as consultas (quer na clínica quer no domicílio) num determinado período de um determinado médico.
         * 
         * @param aDatai    data de início do período pretendido.
         * @param aDataf    data de fim do período pretendido.
         * @param aMedico   médico cujas consultas se pretendem encontrar.
         * @return  array com todas as consultas encontradas.
         */
        public Consulta[] getConsultasMedico(GregorianCalendar aDatai, GregorianCalendar aDataf, Medico aMedico) {
		Cobravel[] entreDatas = getEntreDatas(aDatai,aDataf);
                ArrayList<Consulta> tempConsultas = new ArrayList<Consulta>();
                for(int i=0; i<entreDatas.length; i++){
                    if(entreDatas[i].getClass()==Consulta.class&&((Consulta)entreDatas[i]).getMedicos().contains(aMedico))
                        tempConsultas.add((Consulta)entreDatas[i]);
                }
                Consulta[] consultasEntreDatas = new Consulta[tempConsultas.size()];

                return tempConsultas.toArray(consultasEntreDatas);
	}

        /** Método que determina e devolve uma lista só com os internamentos num determinado período de um determinado médico.
         * 
         * @param aDatai    data de início do período pretendido.
         * @param aDataf    data de fim do período pretendido.
         * @param aMedico   médico cujas consultas se pretendem encontrar.
         * @return  array com todos os internamentos encontrados.
         */
        public Internamento[] getInternamentosMedico(GregorianCalendar aDatai, GregorianCalendar aDataf, Medico aMedico) {
		Cobravel[] entreDatas = getEntreDatas(aDatai,aDataf);
                ArrayList<Consulta> tempInternamento = new ArrayList<Consulta>();
                for(int i=0; i<entreDatas.length; i++){
                    if(entreDatas[i].getClass()==Internamento.class&&((Internamento)entreDatas[i]).getMedicos().contains(aMedico))
                        tempInternamento.add((Consulta)entreDatas[i]);
                }
                Internamento[] InternamentoEntreDatas = new Internamento[tempInternamento.size()];

                return tempInternamento.toArray(InternamentoEntreDatas);
	}


        /** Método que calcula a média de consultas por mês de um determinado médico.
         * 
         * @param aMedico    médico cuja média se pretende calcular.
         * @return  float com a média do número de consultas, ou 0 caso não hajam consultas.
         */
        public float mediaConsultas(Medico aMedico) {
            //Calculo da media, em que os meses totais correspondem ao numero de meses em
            //que houve consultas na clínica. Meses sem consultas nenhumas não entraram na media

            ArrayList<GregorianCalendar> mesesDiferentesComConsultas = new ArrayList<GregorianCalendar>();
            Consulta[] todasConsultas = getConsultas(null, null);
            //um array com todos os meses diferenciaveis
            for(int i=0; i<todasConsultas.length; i++){
                GregorianCalendar esteMes = new GregorianCalendar(todasConsultas[i].getDataf().get(GregorianCalendar.YEAR), todasConsultas[i].getDataf().get(GregorianCalendar.MONTH), 1);
                if(!mesesDiferentesComConsultas.contains(esteMes))
                    mesesDiferentesComConsultas.add(esteMes);
            }
            if(mesesDiferentesComConsultas.size()>0){
                int[] numConsultas = new int[mesesDiferentesComConsultas.size()];
                int totalConsultas = 0;
                //o numero de consultas de determindado médico para cada um dos meses
                for(int j=0; j<mesesDiferentesComConsultas.size(); j++){
                    GregorianCalendar dataFim = new GregorianCalendar(mesesDiferentesComConsultas.get(j).get(GregorianCalendar.YEAR), mesesDiferentesComConsultas.get(j).get(GregorianCalendar.MONTH)+1, 1);
                    numConsultas[j] = getConsultasMedico(mesesDiferentesComConsultas.get(j), dataFim, aMedico).length;
                    //log.outln("numConsultas: "+numConsultas[j]);
                    totalConsultas += numConsultas[j];
                }
                //o total sobre o numero de meses
                return ((float)totalConsultas)/((float)mesesDiferentesComConsultas.size());
            }else {
                return 0;
            }
	}

        /** Método que calcula o desvio padrão do número consultas de um determinado médico.
         * 
         * @param aMedico   médico cuja média se pretende calcular.
         * @return  float com o desvio padrão do número consultas, ou 0 caso só haja uma consulta ou nenhuma.
         */
        public float desvioPadraoConsultas(Medico aMedico) {
            //Calculo do desvio padrão, em que os meses totais correspondem ao numero de meses em
            //que houve consultas na clínica. Meses sem consultas nenhumas n entraram na media
            float media = mediaConsultas(aMedico);

            ArrayList<GregorianCalendar> mesesDiferentesComConsultas = new ArrayList<GregorianCalendar>();
            Consulta[] todasConsultas = getConsultas(null, null);
            for(int i=0; i<todasConsultas.length; i++){
                GregorianCalendar esteMes = new GregorianCalendar(todasConsultas[i].getDataf().get(GregorianCalendar.YEAR), todasConsultas[i].getDataf().get(GregorianCalendar.MONTH), 1);
                if(!mesesDiferentesComConsultas.contains(esteMes))
                    mesesDiferentesComConsultas.add(esteMes);
            }
            if(mesesDiferentesComConsultas.size()>1){
                float[] numParaDesvioPadrao = new float[mesesDiferentesComConsultas.size()];
                float totalParaDesvioPadrao = 0;
                //somatorio de (xi - media)^2
                for(int j=0; j<mesesDiferentesComConsultas.size(); j++){
                    GregorianCalendar dataFim = new GregorianCalendar(mesesDiferentesComConsultas.get(j).get(GregorianCalendar.YEAR), mesesDiferentesComConsultas.get(j).get(GregorianCalendar.MONTH)+1, 1);
                    //(xi - media)^2
                    numParaDesvioPadrao[j] = (float)Math.pow(getConsultasMedico(mesesDiferentesComConsultas.get(j), dataFim, aMedico).length-media, 2);
                    totalParaDesvioPadrao += numParaDesvioPadrao[j];
                }
                //Raiz(somatorio/ N-1)
                return (float)Math.sqrt(((float)totalParaDesvioPadrao)/((float)mesesDiferentesComConsultas.size()-1));
            }else {
                return 0;
            }
	}

        /** Método para adicionar um médico à respetiva lista da clínica.
         * 
         * @param aMedico   objecto Medico com os dados do médico a ser adicionado.
         */
        public void addMedico(Medico aMedico) {
                aMedico.setId(this.idMed++);
		this._medicos.add(aMedico);
	}

        /** Método para retirar um determinado médico da respetiva lista da clínica.
         * 
         * @param aMedico   objecto Medico com os dados do médico a ser retirado.
         */
        public void deleteMedico(Medico aMedico) {
		this._medicos.remove(aMedico);
	}

        /** Método para adicionar um cliente à respetiva lista da clínica.
         * 
         * @param aCliente  objecto Cliente com os dados do cliente a ser adicionado.
         */
        public void addCliente(Cliente aCliente) {
                aCliente.setId(this.idCli++);
                for(int i=0; i<aCliente.getAnimais().size(); i++)
                    aCliente.getAnimais().get(i).setId(this.idAni++);
                
		this._clientes.add(aCliente);
	}

        /** Método para retirar um determinado cliente da respetiva lista da clínica.
         * 
         * @param aCliente  objecto Cliente com os dados do cliente a ser retirado.
         */
        public void deleteCliente(Cliente aCliente) {
		this._clientes.remove(aCliente);
	}

        /** Método para adicionar uma consulta (seja na clínica ou domicílio) à lista de consultas e internamentos da clínica.
         * 
         * @param aConsulta objecto Consulta com os dados da consulta a ser adicionada.
         */
        public void addConsulta(Consulta aConsulta) {
                if(aConsulta.getTipo()==0){
                    //clínica
                    if(aConsulta.getAnimal().getClass()==Estimacao.class)
                        aConsulta.setCusto(this._precoConsultaEstimacao);
                    else
                        aConsulta.setCusto(this._precoConsultaCriacao);
                }else{
                    //domicílio
                    Cliente oCliente = null;
                    for(int i=0; i<clientes().size(); i++){
                        if(clientes().get(i).getAnimais().contains(aConsulta.getAnimal()))
                            oCliente = clientes().get(i);
                    }
                    if(oCliente!=null&&oCliente.listPets().length<=2){
                        if(aConsulta.getAnimal().getClass()==Estimacao.class)
                            aConsulta.setCusto(this._precoConsultaEstimacao+this._precoDeslocacao);
                        else
                            aConsulta.setCusto(this._precoConsultaCriacao+this._precoDeslocacao);
                    }else {
                        aConsulta.setCusto(this._precoDeslocacao);
                    }
                }
		this._eventos.add(aConsulta);
	}

        /** Método para retirar uma consulta (seja na clínica ou domicílio) da lista de consultas e internamentos da clínica.
         * 
         * @param aConsulta objecto Consulta com os dados da consulta a ser retirada.
         */
        public void deleteConsulta(Consulta aConsulta) {
		this._eventos.remove(aConsulta);
	}

        /** Método para adicionar um internamento à lista de consultas e internamentos da clínica.
         * 
         * @param aInternamento objecto Internamento com os dados do internamento a ser adicionada.
         */
        public void addInternamento(Internamento aInternamento) {
                if(aInternamento.isFinalized()){
                    int dias = (int)Math.ceil(((((aInternamento.getDataf().getTimeInMillis()-aInternamento.getDataf().getTimeInMillis())/1000)/60)/60)/24);
                    if(aInternamento.getAnimal().getClass()==Estimacao.class)
                            aInternamento.setCusto(this._precoInternamentoEstimacao*dias);
                        else
                            aInternamento.setCusto(this._precoInternamentoCriacao*dias);
                }
		this._eventos.add(aInternamento);
	}

        /** Método para retirar um internamento da lista de consultas e internamentos da clínica.
         * 
         * @param aInternamento objecto Internamento com os dados do internamento a ser retirado.
         */
        public void deleteInternamento(Internamento aInternamento) {
		this._eventos.remove(aInternamento);
	}

        /** Método set para definir o preço das consultas de animais de estimação.
         * 
         * @param aPrecoConsultaEstimacao   float com o novo preço.
         */
        public void setPrecoConsultaEstimacao(float aPrecoConsultaEstimacao) {
		this._precoConsultaEstimacao = aPrecoConsultaEstimacao;
	}

        /** Método get que devolve o preço das consultas de animais de estimação.
         * 
         * @return  float com o preço.
         */
        public float getPrecoConsultaEstimacao() {
		return this._precoConsultaEstimacao;
	}

        /** Método set para definir o preço das consultas de animais de criação.
         * 
         * @param aPrecoConsultaCriacao float com o novo preço.
         */
        public void setPrecoConsultaCriacao(float aPrecoConsultaCriacao) {
		this._precoConsultaCriacao = aPrecoConsultaCriacao;
	}

        /** Método get que devolve o preço das consultas de animais de criação.
         * 
         * @return  float com o preço.
         */
        public float getPrecoConsultaCriacao() {
		return this._precoConsultaCriacao;
	}

        /** Método set para definir o preço por dia dos internamentos de animais de estimação.
         * 
         * @param aPrecoInternamentoEstimacao   float com o novo preço.
         */
        public void setPrecoInternamentoEstimacao(float aPrecoInternamentoEstimacao) {
		this._precoInternamentoEstimacao = aPrecoInternamentoEstimacao;
	}

        /** Método get que devolve o preço por dia dos internamentos de animais de estimação.
         * 
         * @return  float com o preço.
         */
        public float getPrecoInternamentoEstimacao() {
		return this._precoInternamentoEstimacao;
	}

        /**
         * Atribui valor para o preço por dia de um internamento de um Animal de criação
         * @param aPrecoInternamentoCriacao O novo preço.
         */
        public void setPrecoInternamentoCriacao(float aPrecoInternamentoCriacao) {
		this._precoInternamentoCriacao = aPrecoInternamentoCriacao;
	}

        /** Devolve o valor do preço por dia do internamento de animais de criação.
         * 
         * @return  float com o preço.
         */
        public float getPrecoInternamentoCriacao() {
		return this._precoInternamentoCriacao;
	}

        /** Define preço da deslocação de uma consulta ao domicílio.
         * 
         * @param aPrecoDeslocacao  Novo preço.
         */
        public void setPrecoDeslocacao(float aPrecoDeslocacao) {
		this._precoDeslocacao = aPrecoDeslocacao;
	}

        /** Devolve o preço da deslocação de uma consulta ao domicílio.
         * 
         * @return  float com o preço.
         */
        public float getPrecoDeslocacao() {
		return this._precoDeslocacao;
	}

        //Funcoes para serem usadas no GUI

        /** Junta numa lista descrições (id e nome) dos clientes.
         * 
         * @return  array de String com descrições dos clientes.
         */
        public String[] getInlineDescriptionClientes(){
            String[] retorno = new String[clientes().size()];
            for(int i=0; i<clientes().size(); i++){
                retorno[i] = clientes().get(i).listaString();
            }
            return retorno;
        }

        /** Junta numa lista descrições (id e nome) dos médicos.
         * 
         * @return  array de String com descrições dos médicos.
         */
        public String[] getInlineDescriptionMedicos(){
            String[] retorno = new String[medicos().size()];
            for(int i=0; i<medicos().size(); i++){
                retorno[i] = medicos().get(i).listaString();
            }
            return retorno;
        }

        /** Junta numa lista descrições dos internamentos.
         * 
         * @return  array de String com descrições dos internamentos.
         */
        public String[] getInlineDescriptionInternamentos(){
            String[] retorno = new String[internamentos().size()];
            for(int i=0; i<internamentos().size(); i++){
                retorno[i] = internamentos().get(i).listaString();
            }
            return retorno;
        }

        /** Junta numa lista descrições das consultas.
         * 
         * @return  array de String com descrições das consultas.
         */
        public String[] getInlineDescriptionConsultas(){
            String[] retorno = new String[consultas().size()];
            for(int i=0; i<consultas().size(); i++){
                retorno[i] = consultas().get(i).listaString();
            }
            return retorno;
        }

        /** Junta numa lista descrições (consoante seja de estimação ou criação) dos animais.
         * 
         * @return  array de String com descrições dos animais.
         */
        public String[] getInlineDescriptionAnimais(){
            String[] retorno = new String[animais().size()];
            for(int i=0; i<animais().size(); i++){
                if(animais().get(i).getClass()==Criacao.class)
                    retorno[i] = ((Criacao)animais().get(i)).listaString()+" Tipo: Criação";
                else
                    retorno[i] = ((Estimacao)animais().get(i)).listaString()+" Tipo: Estimação";
            }
            return retorno;
        }



        /** Devolve lista com todos os clientes da clínica.
         * 
         * @return  ArrayList com os clientes.
         */
        public ArrayList<Cliente> clientes(){
            return _clientes;
        }

        /** Devolve lista com todos os médicos da clínica.
         * 
         * @return  ArrayList com os médicos.
         */
        public ArrayList<Medico> medicos(){
            return _medicos;
        }

        /** Devolve lista com todas as consultas e internamentos da clínica.
         * 
         * @return  ArrayList com as consultas e internamentos.
         */
        public ArrayList<Cobravel> eventos(){
            return _eventos;
        }

        /** Devolve lista com todos os animais de todos os clientes da clínica.
         * 
         * @return  ArrayList com os animais.
         */
        public ArrayList<Animal> animais(){
            ArrayList<Animal> retorno = new ArrayList<Animal>();
            for(int i=0; i<clientes().size(); i++){
                retorno.addAll(clientes().get(i).getAnimais());
            }
            return retorno;
        }

        /** Devolve lista com todos os internamentos da clínica.
         * 
         * @return  ArrayList com os internamentos.
         */
        public ArrayList<Internamento> internamentos(){

            Cobravel[] todosEventos = new Cobravel[this._eventos.size()];
            this._eventos.toArray(todosEventos);

            //ordena por data de fim do evento
            Arrays.sort(todosEventos);

            ArrayList<Internamento> tempInternamentos = new ArrayList<Internamento>();
                for(int i=0; i<todosEventos.length; i++){
                    if(todosEventos[i].getClass()==Internamento.class)
                        tempInternamentos.add((Internamento)todosEventos[i]);
                }
            return tempInternamentos;
        }

        /** Devolve lista com todas as consultas da clínica.
         * 
         * @return  ArrayList com as consultas
         */
        public ArrayList<Consulta> consultas(){

            Cobravel[] todosEventos = new Cobravel[this._eventos.size()];
            this._eventos.toArray(todosEventos);

            //ordena por data de fim do evento
            Arrays.sort(todosEventos);

            ArrayList<Consulta> tempConsulta = new ArrayList<Consulta>();
                for(int i=0; i<todosEventos.length; i++){
                    if(todosEventos[i].getClass()==Consulta.class)
                        tempConsulta.add((Consulta)todosEventos[i]);
                }
            return tempConsulta;
        }

}